package common

import (
	"../web"
	"bytes"
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"path/filepath"
)

const (
	DefaultLanguage = "zh_CN"
)

var (
	Templates_chn = map[string]*template.Template{}
	Templates_eng = map[string]*template.Template{}
	Config        map[string]string
	Application   = web.NewServer()
)

func ParseTemplates(lang, file string) *template.Template {
	var t *template.Template
	files := []string{filepath.Join("template", lang, "base.html"), filepath.Join("template", lang, file)}
	for _, filename := range files {
		b, err := ioutil.ReadFile(filename)
		if err != nil {
			panic(err)
		}

		s := string(b)
		var tmpl *template.Template
		if t == nil {
			t = template.New(filename)
		}
		if filename == t.Name() {
			tmpl = t
		} else {
			tmpl = t.New(filename)
		}
		_, err = tmpl.Parse(s)
		if err != nil {
			panic(err)
		}
	}

	return t
}

func writeResponseWriter(ctx *web.Context, rb []byte) {
	ctx.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
	ctx.ResponseWriter.Header().Set("Accept-Encoding", "gzip")
	ctx.ResponseWriter.Header().Set("Content-Encoding", "gzip")
	output_writer := gzip.NewWriter(ctx.ResponseWriter)
	defer output_writer.Close()
	output_writer.Write(rb)
}

func RenderTemplate(ctx *web.Context, data *map[string]interface{}, file string) {
	lang := DefaultLanguage
	if usr := CurrentUser(ctx); usr != nil {
		lang = usr.Language

		(*data)["lstOper"] = usr.ROper
		(*data)["lstQuery"] = usr.RQuery
		(*data)["lstWono"] = usr.RWono
		(*data)["lstSets"] = usr.RSets
		(*data)["lstUser"] = usr.RUser
		(*data)["realname"] = usr.Realname
	}

	teplDict := Templates_chn
	if lang == "en_US" {
		teplDict = Templates_eng
	}

	t := teplDict[file]

	var buf bytes.Buffer
	t.Execute(&buf, data)

	writeResponseWriter(ctx, buf.Bytes())
}

func RenderSingleTemplate(ctx *web.Context, data *map[string]interface{}, file string) {
	lang := DefaultLanguage
	if usr := CurrentUser(ctx); usr != nil {
		lang = usr.Language
	}

	b, err := ioutil.ReadFile(filepath.Join("template", lang, file))
	if err != nil {
		panic(err)
	}

	s := string(b)
	t := template.New(file)

	_, err = t.Parse(s)
	if err != nil {
		panic(err)
	}

	var buf bytes.Buffer
	t.Execute(&buf, data)

	writeResponseWriter(ctx, buf.Bytes())
}

func EncryptPassword(password string) string {
	h := sha1.New()
	io.WriteString(h, password)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func CurrentUser(ctx *web.Context) *User {
	ss, flg := ctx.GetSecureCookie("sfcsession")
	if !flg {
		return nil
	}
	sess := SessionFromId(ss)
	if !sess.Exists() {
		return nil
	}

	user, err := sess.Get("user")
	if err != nil {
		return nil
	}

	sess.UpdateSession()

	return user.(*User)
}

func GetUser(ctx *web.Context) (*User, bool) {
	usr := CurrentUser(ctx)
	if usr == nil {
		ctx.Redirect(http.StatusFound, "/login")
		return nil, false
	}

	return usr, true
}

func rightCheck(usr *User, right string) bool {
	for _, url := range usr.ROper {
		if _, ok := url[right]; ok {
			return true
		}
	}
	for _, url := range usr.RQuery {
		if _, ok := url[right]; ok {
			return true
		}
	}
	for _, url := range usr.RWono {
		if _, ok := url[right]; ok {
			return true
		}
	}
	for _, url := range usr.RSets {
		if _, ok := url[right]; ok {
			return true
		}
	}
	for _, url := range usr.RUser {
		if _, ok := url[right]; ok {
			return true
		}
	}

	return false
}

func getFlash(ctx *web.Context, tmplData map[string]interface{}) {
	ss, flg := ctx.GetSecureCookie("sfcsession")
	if !flg {
		return
	}

	sess := SessionFromId(ss)
	if !sess.Exists() {
		return
	}

	flash, err := sess.Get("flash")
	if err == nil {
		tmplData["flash"] = flash
		sess.Put("flash", "")
	}
}

func setFlash(ctx *web.Context, info string) {
	ss, flg := ctx.GetSecureCookie("sfcsession")
	if !flg {
		return
	}

	sess := SessionFromId(ss)
	if !sess.Exists() {
		return
	}

	sess.Put("flash", info)
}

func GetScanStatus(ctx *web.Context, station string) bool {
	ss, flg := ctx.GetSecureCookie("sfcsession")
	if !flg {
		ctx.Redirect(http.StatusFound, "/login")
		return true
	}

	sess := SessionFromId(ss)
	if !sess.Exists() {
		ctx.Redirect(http.StatusFound, "/login")
		return true
	}

	status, err := sess.Get(station)
	if err == nil {
		return status.(bool)
	}

	return true
}

func SetScanStatus(ctx *web.Context, station string, status bool) {
	ss, flg := ctx.GetSecureCookie("sfcsession")
	if !flg {
		ctx.Redirect(http.StatusFound, "/login")
		return
	}

	sess := SessionFromId(ss)
	if !sess.Exists() {
		ctx.Redirect(http.StatusFound, "/login")
		return
	}

	sess.Put(station, status)
}

func ForbiddenHandler(ctx *web.Context) {
	RenderTemplate(ctx, &map[string]interface{}{}, "403.html")
}

func internalErrorHandler(ctx *web.Context) {
	RenderTemplate(ctx, &map[string]interface{}{}, "500.html")
}

func notFoundHandler(ctx *web.Context) {
	_, flg := GetUser(ctx)
	if flg {
		RenderTemplate(ctx, &map[string]interface{}{}, "404.html")
	}
}

type BaseHandler struct {
	Ctx        *web.Context
	TmplData   map[string]interface{}
	Url        string
	Rightlist  []string
	Html       string
	Single     string // for have no base.html
	Lang       string
	Usr        *User
	Sqlparas   []interface{}
	Flashstr   string
	Flashparas []interface{}
}

type IBaseHandler interface {
	GetUser()
	RightCheck(string) bool
	GetFlash()
	Prepare()
	ParameterCheck() bool
	SqlExecute() bool
	SqlQueryExecute()
	SetFlash(string, ...interface{})
	RenderTemplate()
	RedirectUrl()
	Do(IBaseHandler)
	DoJson(IBaseHandler)
	DoQuery(IBaseHandler)
	DoRender(IBaseHandler)
	DoQueryUrl(IBaseHandler)
	DoJsonQuery(IBaseHandler)
	DoRedirectUrl(IBaseHandler)
}

func (self *BaseHandler) GetUser() {
	usr, flg := GetUser(self.Ctx)
	if flg {
		self.Usr = usr
		self.Lang = usr.Language
	} else {
		self.Usr = nil
	}
}

func (self *BaseHandler) RightCheck(rights string) bool {
	if self.Usr == nil {
		return false
	}
	if rightCheck(self.Usr, rights) {
		return true
	}
	return false
}

func (self *BaseHandler) GetFlash() {
	getFlash(self.Ctx, self.TmplData)
}

func (self *BaseHandler) Prepare() {}

func (self *BaseHandler) ParameterCheck() bool {
	return true
}

func (self *BaseHandler) SqlExecute() bool {
	return true
}

func (self *BaseHandler) SqlQueryExecute() {}

func (self *BaseHandler) SetFlash(trans string, param ...interface{}) {
	setFlash(self.Ctx, GetText(self.Lang, trans, param...))
}

func (self *BaseHandler) RenderTemplate() {
	if self.Single != "" {
		RenderSingleTemplate(self.Ctx, &self.TmplData, self.Single)
		return
	}
	RenderTemplate(self.Ctx, &self.TmplData, self.Html)
}

func (self *BaseHandler) RedirectUrl() {
	self.Ctx.Redirect(http.StatusFound, self.Url)
}

func (self *BaseHandler) Do(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	self.GetUser()
	if self.Usr == nil {
		return
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	for _, r := range self.Rightlist {
		if self.RightCheck(r) {
			break
		}
		ForbiddenHandler(self.Ctx)
		return
	}

	self.GetFlash()
	obj.Prepare()

	if self.Ctx.Request.Method == "POST" {
		if !obj.ParameterCheck() {
			self.RenderTemplate()
			return
		}

		if obj.SqlExecute() {
			self.SetFlash(self.Flashstr, self.Flashparas...)
			self.RedirectUrl()
		} else {
			self.RenderTemplate()
		}

		return
	}

	self.RenderTemplate()
}

func (self *BaseHandler) DoRender(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	self.GetUser()
	if self.Usr == nil {
		return
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	for _, r := range self.Rightlist {
		if self.RightCheck(r) {
			break
		}
		ForbiddenHandler(self.Ctx)
		return
	}

	obj.Prepare()

	if self.Ctx.Request.Method == "POST" {
		if !obj.ParameterCheck() {
			self.RenderTemplate()
			return
		}

		if obj.SqlExecute() {
			self.SetFlash(self.Flashstr, self.Flashparas...)
			self.GetFlash()
		}

		self.RenderTemplate()

		return
	}

	self.RenderTemplate()
}

func (self *BaseHandler) DoJson(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	usr := CurrentUser(self.Ctx)

	self.Ctx.ResponseWriter.Header().Set("content-type", "application/json")

	if usr == nil {
		dict_js, _ := json.Marshal(map[string]string{"result": "RELOGIN"})
		self.Ctx.ResponseWriter.Write(dict_js)
		return
	} else {
		self.Usr = usr
		self.Lang = usr.Language
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	if !obj.ParameterCheck() {
		return
	}

	obj.SqlQueryExecute()
}

func (self *BaseHandler) DoQuery(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	self.GetUser()
	if self.Usr == nil {
		return
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	for _, r := range self.Rightlist {
		if self.RightCheck(r) {
			break
		}
		ForbiddenHandler(self.Ctx)
		return
	}

	obj.Prepare()

	if !obj.ParameterCheck() {
		self.RenderTemplate()
		return
	}
	if self.Ctx.Request.Method == "POST" {
		obj.SqlQueryExecute()
	}

	self.RenderTemplate()
}

func (self *BaseHandler) DoQueryUrl(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	self.GetUser()
	if self.Usr == nil {
		return
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	for _, r := range self.Rightlist {
		if self.RightCheck(r) {
			break
		}
		ForbiddenHandler(self.Ctx)
		return
	}

	if !obj.ParameterCheck() {
		return
	}

	obj.SqlQueryExecute()

	self.RenderTemplate()
}

func (self *BaseHandler) DoJsonQuery(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	usr := CurrentUser(self.Ctx)

	self.Ctx.ResponseWriter.Header().Set("content-type", "application/json")

	if usr == nil {
		return
	} else {
		self.Usr = usr
		self.Lang = usr.Language
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	if !obj.ParameterCheck() {
		return
	}

	obj.SqlQueryExecute()
}

func (self *BaseHandler) DoRedirectUrl(obj IBaseHandler) {
	self.TmplData = make(map[string]interface{})

	self.GetUser()
	if self.Usr == nil {
		return
	}

	self.TmplData["ProductionLine"] = self.Usr.DBName

	for _, r := range self.Rightlist {
		if self.RightCheck(r) {
			break
		}
		ForbiddenHandler(self.Ctx)
		return
	}

	if !obj.ParameterCheck() {
		return
	}

	obj.SqlQueryExecute()

	self.RedirectUrl()
}
